#include <asm.h>

# Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.
# 启动CPU：切换到32位保护模式，跳转到C语言。
# BIOS将这段代码从硬盘的第一个扇区加载到物理地址0x7c00的内存中，
# 并在实模式下执行，%cs=0，%ip=7c00


.set PROT_MODE_CSEG,        0x8                     # kernel code segment selector 定义PROT_MODE_CSEG 的常量，其值为 0x8
.set PROT_MODE_DSEG,        0x10                    # kernel data segment selector 定义PROT_MODE_DSEG 的常量，其值为 0x10
.set CR0_PE_ON,             0x1                     # protected mode enable flag 受保护模式启用标志 定义CR0_PE_ON 的常量，其值为 0x1
.set SMAP,                  0x534d4150

# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
# 起始地址应该是 0:7c00，在实模式下，这是运行的引导加载程序的起始地址。
.globl start  # 表示将 start 标签声明为全局可见的入口点。
start:  # 定义 start 标签
.code16                                             # Assemble for 16-bit mode  指示编译器将后续代码编译为 16 位模式
    cli                                             # Disable interrupts  禁用中断
    cld                                             # String operations increment 清除方向标志（DF），使字符串操作在内存中从低地址到高地址进行增量操作。

    # Set up the important data segment registers (DS, ES, SS).
    xorw %ax, %ax                                   # Segment number zero 将寄存器 AX 清零,准备将其作为段寄存器的值。
    movw %ax, %ds                                   # -> Data Segment 将 AX（值为 0）存入数据段寄存器 DS，初始化数据段为 0。
    movw %ax, %es                                   # -> Extra Segment 将 AX（仍然是 0）存入附加段寄存器 ES，初始化附加段为 0。
    movw %ax, %ss                                   # -> Stack Segment 将 AX（仍然是 0）存入栈段寄存器 SS，初始化栈段为 0。

    # Enable A20:
    #  For backwards compatibility with the earliest PCs, physical
    #  address line 20 is tied low, so that addresses higher than
    #  1MB wrap around to zero by default. This code undoes this.
seta20.1:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty). 设置标签 seta20.1，从 I/O 端口 0x64 读取数据到寄存器 AL，等待8042控制器输入缓冲区为空。
    testb $0x2, %al
    jnz seta20.1           # 测试 AL 中的第 2 位，如果不为零，则跳回 seta20.1，表示8042仍在忙。

    movb $0xd1, %al                                 # 0xd1 -> port 0x64 将值 0xd1 存入 AL，这个值将被写入端口 0x64。
    outb %al, $0x64                                 # 0xd1 means: write data to 8042's P2 port 将 AL 的值（即 0xd1）写入端口 0x64

seta20.2:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty). 设置标签 seta20.2，同样从端口 0x64 读取数据到 AL，等待8042输入缓冲区为空。
    testb $0x2, %al
    jnz seta20.2     # 再次测试 AL 中的第 2 位，如果不为零，则跳回 seta20.2。

    movb $0xdf, %al                                 # 0xdf -> port 0x60 将值 0xdf 存入 AL，此值将被写入端口 0x60。
    outb %al, $0x60                                 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1 将 AL 的值（即 0xdf）写入端口 0x60，这将设置8042控制器的 A20 地址线为高（1），允许访问超过1MB的内存。

probe_memory:
    movl $0, 0x8000
    xorl %ebx, %ebx
    movw $0x8004, %di
start_probe:
    movl $0xE820, %eax
    movl $20, %ecx
    movl $SMAP, %edx
    int $0x15
    jnc cont
    movw $12345, 0x8000
    jmp finish_probe
cont:
    addw $20, %di
    incl 0x8000
    cmpl $0, %ebx
    jnz start_probe
finish_probe:

    # Switch from real to protected mode, using a bootstrap GDT
    # and segment translation that makes virtual addresses
    # identical to physical addresses, so that the
    # effective memory map does not change during the switch.
    lgdt gdtdesc   #加载全局描述符表寄存器（GDTR）到 gdtdesc，以便设置保护模式的段描述符。
    movl %cr0, %eax #将控制寄存器 CR0 的值加载到寄存器 EAX 中
    orl $CR0_PE_ON, %eax  #将 CR0 中的保护模式开启标志（CR0_PE_ON）与 EAX 相或，以便设置 CR0 的保护模式位。
    movl %eax, %cr0  #将修改后的 EAX 的值存回 CR0，使处理器进入保护模式。

    # Jump to next instruction, but in 32-bit code segment.
    # Switches processor into 32-bit mode.
    ljmp $PROT_MODE_CSEG, $protcseg  #执行远跳转到保护模式的代码段 PROT_MODE_CSEG 和偏移地址 protcseg，实现模式切换并开始执行32位代码。

.code32                                             # Assemble for 32-bit mode
protcseg:   #定义标签 protcseg，表示保护模式下的代码段开始。
    # Set up the protected-mode data segment registers
    movw $PROT_MODE_DSEG, %ax                       # Our data segment selector 将数据段选择子 PROT_MODE_DSEG 的值加载到寄存器 AX 中。
    movw %ax, %ds                                   # -> DS: Data Segment  将 AX 中的数据段选择子存入数据段寄存器 DS,设置数据段
    movw %ax, %es                                   # -> ES: Extra Segment  将 AX 中的数据段选择子存入附加段寄存器 ES，设置附加段
    movw %ax, %fs                                   # -> FS 将 AX 中的数据段选择子存入段寄存器 FS，设置 FS 段。
    movw %ax, %gs                                   # -> GS 将 AX 中的数据段选择子存入段寄存器 GS
    movw %ax, %ss                                   # -> SS: Stack Segment 将 AX 中的数据段选择子存入栈段寄存器 SS，设置栈段。

    # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
    movl $0x0, %ebp
    movl $start, %esp
    call bootmain

    # If bootmain returns (it shouldn't), loop.
spin:
    jmp spin

.data
# Bootstrap GDT
.p2align 2                                          # force 4 byte alignment
gdt:
    SEG_NULLASM                                     # null seg
    SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)           # code seg for bootloader and kernel
    SEG_ASM(STA_W, 0x0, 0xffffffff)                 # data seg for bootloader and kernel

gdtdesc:
    .word 0x17                                      # sizeof(gdt) - 1
    .long gdt                                       # address gdt
